package com.stark.commons.spring.core.support.aop;

import java.lang.reflect.Method;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import lombok.Getter;

/**
 * 作用于<strong>方法上</strong>的注解的 spel 表达式解析器。
 * <p>通过 {@link #getValue(String)} 获取解析值。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
public class MethodExpressionEvaluator {
	
	private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

	private final EvaluationContext evaluationContext;
	
	@Getter
	private final Class<?> targetClass;
	
	@Getter
	private final Method targetMethod;
	
	public MethodExpressionEvaluator(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        this.targetClass = getTargetClass(joinPoint.getTarget());
        this.targetMethod = getTargetMethod(targetClass, method);
        MethodExpressionRootObject rootObject = new MethodExpressionRootObject(
        		targetMethod, joinPoint.getArgs(), joinPoint.getTarget(), targetClass);
		this.evaluationContext = new MethodBasedEvaluationContext(
				rootObject, method, joinPoint.getArgs(), new DefaultParameterNameDiscoverer());
	}
	
	/**
	 * 解析表达式。
	 * @param expression 表达式。
	 * @return 解析后的值。
	 */
	public Object getValue(String expression) {
		if (StringUtils.isBlank(expression)) {
			return "";
		}
		
		Object value = spelExpressionParser.parseExpression(expression).getValue(evaluationContext);
        return value;
	}
	
	/**
	 * 获取实际执行类。
	 * @param target 类对象。
	 * @return 实际执行类。
	 */
	private Class<?> getTargetClass(Object target) {
		Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
		if (targetClass == null && target != null) {
			targetClass = target.getClass();
		}
		return targetClass;
	}
	
	/**
	 * 获取实际执行方法。
	 * @param targetClass 类。
	 * @param method 方法。
	 * @return 实际执行方法。
	 */
	private Method getTargetMethod(Class<?> targetClass, Method method) {
		return AopUtils.getMostSpecificMethod(method, targetClass);
	}

}
